'use strict'

var is = require('bpmn-js/lib/util/ModelUtil').is

var elementHelper = require('../../../../helper/ElementHelper'),
  inputOutputHelper = require('../../../../helper/InputOutputHelper'),
  cmdHelper = require('../../../../helper/CmdHelper'),
  utils = require('../../../../Utils')

var entryFactory = require('../../../../factory/EntryFactory'),
  scriptImplementation = require('./Script')

function createElement(type, parent, factory, properties) {
  return elementHelper.createElement(type, properties, parent, factory)
}

function isScript(elem) {
  return is(elem, 'activiti:Script')
}

function isList(elem) {
  return is(elem, 'activiti:List')
}

function isMap(elem) {
  return is(elem, 'activiti:Map')
}

function ensureInputOutputSupported(element, insideConnector) {
  return inputOutputHelper.isInputOutputSupported(element, insideConnector)
}

module.exports = function(element, bpmnFactory, options, translate) {

  var typeInfo = {
    'activiti:Map': {
      value: 'map',
      label: translate('Map')
    },
    'activiti:List': {
      value: 'list',
      label: translate('List')
    },
    'activiti:Script': {
      value: 'script',
      label: translate('Script')
    }
  }

  options = options || {}

  var insideConnector = !!options.insideConnector,
    idPrefix = options.idPrefix || ''

  var getSelected = options.getSelectedParameter

  if (!ensureInputOutputSupported(element, insideConnector)) {
    return []
  }

  var entries = []

  var isSelected = function(element, node) {
    return getSelected(element, node)
  }

  // parameter name ////////////////////////////////////////////////////////

  entries.push(entryFactory.validationAwareTextField({
    id: idPrefix + 'parameterName',
    label: translate('Name'),
    modelProperty: 'name',

    getProperty: function(element, node) {
      return (getSelected(element, node) || {}).name
    },

    setProperty: function(element, values, node) {
      var param = getSelected(element, node)
      return cmdHelper.updateBusinessObject(element, param, values)
    },

    validate: function(element, values, node) {
      var bo = getSelected(element, node)

      var validation = {}
      if (bo) {
        var nameValue = values.name

        if (nameValue) {
          if (utils.containsSpace(nameValue)) {
            validation.name = translate('Name must not contain spaces')
          }
        } else {
          validation.name = translate('Parameter must have a name')
        }
      }

      return validation
    },

    hidden: function(element, node) {
      return !isSelected(element, node)
    }
  }))

  // parameter type //////////////////////////////////////////////////////

  var selectOptions = [
    { value: 'text', name: translate('Text') },
    { value: 'script', name: translate('Script') },
    { value: 'list', name: translate('List') },
    { value: 'map', name: translate('Map') }
  ]

  entries.push(entryFactory.selectBox({
    id: idPrefix + 'parameterType',
    label: translate('Type'),
    selectOptions: selectOptions,
    modelProperty: 'parameterType',

    get: function(element, node) {
      var bo = getSelected(element, node)

      var parameterType = 'text'

      if (typeof bo !== 'undefined') {
        var definition = bo.get('definition')
        if (typeof definition !== 'undefined') {
          var type = definition.$type
          parameterType = typeInfo[type].value
        }
      }

      return {
        parameterType: parameterType
      }
    },

    set: function(element, values, node) {
      var bo = getSelected(element, node)

      var properties = {
        value: undefined,
        definition: undefined
      }

      var createParameterTypeElem = function(type) {
        return createElement(type, bo, bpmnFactory)
      }

      var parameterType = values.parameterType

      if (parameterType === 'script') {
        properties.definition = createParameterTypeElem('activiti:Script')
      } else if (parameterType === 'list') {
        properties.definition = createParameterTypeElem('activiti:List')
      } else if (parameterType === 'map') {
        properties.definition = createParameterTypeElem('activiti:Map')
      }

      return cmdHelper.updateBusinessObject(element, bo, properties)
    },

    show: function(element, node) {
      return isSelected(element, node)
    }

  }))

  // parameter value (type = text) ///////////////////////////////////////////////////////

  entries.push(entryFactory.textBox({
    id: idPrefix + 'parameterType-text',
    label: translate('Value'),
    modelProperty: 'value',
    get: function(element, node) {
      return {
        value: (getSelected(element, node) || {}).value
      }
    },

    set: function(element, values, node) {
      var param = getSelected(element, node)
      values.value = values.value || undefined
      return cmdHelper.updateBusinessObject(element, param, values)
    },

    show: function(element, node) {
      var bo = getSelected(element, node)
      return bo && !bo.definition
    }

  }))

  // parameter value (type = script) ///////////////////////////////////////////////////////
  var script = scriptImplementation('scriptFormat', 'value', true, translate)
  entries.push({
    id: idPrefix + 'parameterType-script',
    html: '<div data-show="isScript">' +
      script.template +
      '</div>',
    get: function(element, node) {
      var bo = getSelected(element, node)
      return bo && isScript(bo.definition) ? script.get(element, bo.definition) : {}
    },

    set: function(element, values, node) {
      var bo = getSelected(element, node)
      var update = script.set(element, values)
      return cmdHelper.updateBusinessObject(element, bo.definition, update)
    },

    validate: function(element, values, node) {
      var bo = getSelected(element, node)
      return bo && isScript(bo.definition) ? script.validate(element, bo.definition) : {}
    },

    isScript: function(element, node) {
      var bo = getSelected(element, node)
      return bo && isScript(bo.definition)
    },

    script: script

  })

  // parameter value (type = list) ///////////////////////////////////////////////////////

  entries.push(entryFactory.table({
    id: idPrefix + 'parameterType-list',
    modelProperties: ['value'],
    labels: [translate('Value')],
    addLabel: translate('Add Value'),

    getElements: function(element, node) {
      var bo = getSelected(element, node)

      if (bo && isList(bo.definition)) {
        return bo.definition.items
      }

      return []
    },

    updateElement: function(element, values, node, idx) {
      var bo = getSelected(element, node)
      var item = bo.definition.items[idx]
      return cmdHelper.updateBusinessObject(element, item, values)
    },

    addElement: function(element, node) {
      var bo = getSelected(element, node)
      var newValue = createElement('activiti:Value', bo.definition, bpmnFactory, { value: undefined })
      return cmdHelper.addElementsTolist(element, bo.definition, 'items', [newValue])
    },

    removeElement: function(element, node, idx) {
      var bo = getSelected(element, node)
      return cmdHelper.removeElementsFromList(element, bo.definition, 'items', null, [bo.definition.items[idx]])
    },

    editable: function(element, node, prop, idx) {
      var bo = getSelected(element, node)
      var item = bo.definition.items[idx]
      return !isMap(item) && !isList(item) && !isScript(item)
    },

    setControlValue: function(element, node, input, prop, value, idx) {
      var bo = getSelected(element, node)
      var item = bo.definition.items[idx]

      if (!isMap(item) && !isList(item) && !isScript(item)) {
        input.value = value
      } else {
        input.value = typeInfo[item.$type].label
      }
    },

    show: function(element, node) {
      var bo = getSelected(element, node)
      return bo && bo.definition && isList(bo.definition)
    }

  }))

  // parameter value (type = map) ///////////////////////////////////////////////////////

  entries.push(entryFactory.table({
    id: idPrefix + 'parameterType-map',
    modelProperties: ['key', 'value'],
    labels: [translate('Key'), translate('Value')],
    addLabel: translate('Add Entry'),

    getElements: function(element, node) {
      var bo = getSelected(element, node)

      if (bo && isMap(bo.definition)) {
        return bo.definition.entries
      }

      return []
    },

    updateElement: function(element, values, node, idx) {
      var bo = getSelected(element, node)
      var entry = bo.definition.entries[idx]

      if (isMap(entry.definition) || isList(entry.definition) || isScript(entry.definition)) {
        values = {
          key: values.key
        }
      }

      return cmdHelper.updateBusinessObject(element, entry, values)
    },

    addElement: function(element, node) {
      var bo = getSelected(element, node)
      var newEntry = createElement('activiti:Entry', bo.definition, bpmnFactory, { key: undefined, value: undefined })
      return cmdHelper.addElementsTolist(element, bo.definition, 'entries', [newEntry])
    },

    removeElement: function(element, node, idx) {
      var bo = getSelected(element, node)
      return cmdHelper.removeElementsFromList(element, bo.definition, 'entries', null, [bo.definition.entries[idx]])
    },

    editable: function(element, node, prop, idx) {
      var bo = getSelected(element, node)
      var entry = bo.definition.entries[idx]
      return prop === 'key' || (!isMap(entry.definition) && !isList(entry.definition) && !isScript(entry.definition))
    },

    setControlValue: function(element, node, input, prop, value, idx) {
      var bo = getSelected(element, node)
      var entry = bo.definition.entries[idx]

      if (prop === 'key' || (!isMap(entry.definition) && !isList(entry.definition) && !isScript(entry.definition))) {
        input.value = value
      } else {
        input.value = typeInfo[entry.definition.$type].label
      }
    },

    show: function(element, node) {
      var bo = getSelected(element, node)
      return bo && bo.definition && isMap(bo.definition)
    }

  }))

  return entries

}
